package com.luoke.connect;


import cn.hutool.core.map.BiMap;
import cn.hutool.json.JSONUtil;
import com.luoke.constant.ModuleTypeConstant;
import com.luoke.context.GlobalDataStore;
import com.luoke.dao.impl.UserDaoImpl;
import com.luoke.handler.BattleResponseHandler;
import com.luoke.handler.BattleRoundResponseHandler;
import com.luoke.handler.UserResponseHandler;
import com.luoke.result.Result;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 服务端通信主类
 */
public class GameWebSocketServer extends WebSocketServer {

    /**
     * 用来存储每个客户端的最新心跳时间
     */
    private final ConcurrentHashMap<WebSocket, Long> clientHeartbeatMap = new ConcurrentHashMap<>();

    /**
     * 这是一个用于执行定时任务的线程池
     */
    private final ScheduledExecutorService heartbeatChecker = Executors.newScheduledThreadPool(10);


    /**
     * 构造函数，指定监听的端口
     * @param port 端口号
     */
    public GameWebSocketServer(int port) {
        super(new InetSocketAddress(port));
    }


    /**
     * 当有新的客户端连接时调用
     */
    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        clientHeartbeatMap.put(conn, System.currentTimeMillis());
    }




    /**
     * 当有客户端连接关闭时调用
     */
    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        //当客户端连接关闭的时候这时候我们要把在线用户的在线状态设置为下线
        //使用Hutool工具类的map双向查找功能，通过值找键
        BiMap<String, WebSocket> biMap = new BiMap<>(GlobalDataStore.onlineUser);
        String username = biMap.getKey(conn);
        if (username != null) { //这里要判断一下，如果用户是通过正规退出登录入口退出的，而不是通过关闭整个应用程序强制退出，就不要执行下面的代码，防止空指针异常
            GlobalDataStore.onlineUser.remove(username); //在全局中删除该用户
            GlobalDataStore.waitUser.remove(username);//移除等待区
            new UserDaoImpl().updateOnlineStatus(username, false);
            System.out.println(username+" 下线了");
        }

        clientHeartbeatMap.remove(conn);

    }



    /**
     * 当接收到客户端发送的消息时调用
     * @param conn    发生此事件的 WebSocket 实例
     * @param message 收到的 UTF-8 解码消息
     */
    @Override
    public void onMessage(WebSocket conn, String message) {
        //这里首先进行心跳机制的判断，保证客户端的长连接
        if ("ping".equals(message)) {
            conn.send("pong");
            // 记录客户端最近心跳时间
            clientHeartbeatMap.put(conn, System.currentTimeMillis());
            BiMap<String, WebSocket> biMap = new BiMap<>(GlobalDataStore.onlineUser);
            String username = biMap.getKey(conn);
            System.out.println(username+" ping");
        }else {
        Result result = JSONUtil.toBean(message, Result.class);
        //这里判断是什么模块
        if (result.getModuleName().equals(ModuleTypeConstant.USER)) {
            new UserResponseHandler().handler(conn,result);
        } else if (result.getModuleName().equals(ModuleTypeConstant.BATTLE)) {
            new BattleResponseHandler().handler(conn,result);
        } else if (result.getModuleName().equals(ModuleTypeConstant.BATTLE_ROUND)) {
            new BattleRoundResponseHandler().handler(conn,result);
        }
        }



    }

    /**
     * 当发生错误时调用
     */
    @Override
    public void onError(WebSocket conn, Exception ex) {

    }



    /**
     * 服务器启动后调用
     */
    @Override
    public void onStart() {
        startHeartbeatChecker(); // 启动服务端心跳检查
    }





    // 定期检查客户端心跳
    private void startHeartbeatChecker() {
        heartbeatChecker.scheduleAtFixedRate(() -> {
            long now = System.currentTimeMillis();
            clientHeartbeatMap.forEach((conn, lastHeartbeat) -> {
                if (now - lastHeartbeat > 10000) { // 超过10秒未收到心跳
                    System.out.println("Client timed out: " + conn);
                    clientHeartbeatMap.remove(conn);
                    try {
                        conn.close();
                    } catch (Exception e) {
                        System.err.println("Error closing connection: " + e.getMessage());
                    }
                }
            });
        }, 0, 5, TimeUnit.SECONDS); // 每3秒检查一次
    }
}
